package com.example.clientend.utils;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.utils.StringUtils;
import com.example.clientend.config.WebSocketConfig;
import com.example.clientend.domain.ComputerInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import java.net.URI;
import java.util.Date;
import java.util.List;

/**
 * 向日葵sdk-WebSocket连接工具类
 */
@Component
@Slf4j
public class SLWebSocketHandler {

    @Autowired
    InfoGetter infoGetter;
    @Autowired
    RestTemplate restTemplate;

    // 主控端接收发起远控操作回应的接口
    @Value("${url.installServiceUrl}")
    private String installServiceUrl;
    // 主控端接收结束远控操作回应的接口
    @Value("${url.uninstallServiceUrl}")
    private String uninstallServiceUrl;

    // WebSocket会话session，使用全局变量保证连接唯一且长期保持
    private Session session = null;
    // 指令id编号，每发一次指令id编号加1，登出时重置为0
    private long id = 0;
    // 当前远控会话id，默认为-1表示未远控，生产远控会话后更新
    public int sessionId = -1;
    // 服务器返回数据的json字符串，默认为null。当receivedMessage不为null时，说明服务器返回了数据，取走数据进行消费并重置receivedMessage为null。
    public static String receivedMessage = null;
    
    /**
     * 连接WebSocket
     * 使用@PostConstruct注解，在系统开始运行时就创建WebSocket连接
     */
    @PostConstruct
    public void connect(){
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        try {
            session = container.connectToServer(WebSocketConfig.class, URI.create("ws://localhost:9999"));
        }catch (Exception e){
            log.error("WebSocket连接异常！");
        }
    }
    
    /**
     * 获取向日葵sdk当前状态
     * @return
     */
    public String getStatus(){
        // 判断session是否已创建，未连接则创建连接
        if (session == null || !session.isOpen()){
            this.connect();
        }
        
        JSONObject json = new JSONObject();
        json.put("id",id++);
        json.put("name","get_status");
        
        try {
            session.getBasicRemote().sendText(json.toJSONString());
            return this.waitForResponse();
        }catch (Exception e){
            log.error("getStatus异常！");
            log.error(e.getMessage());
            return null;
        }
    }
    
    /**
     * 使用license登录向日葵sdk
     * @return
     */
    public String loginWithLicense(){
        JSONObject json = new JSONObject();
        json.put("id",id++);
        json.put("name","login_license");
        JSONObject data = new JSONObject();
        data.put("address","PHSRC://slp2p.gzport.com:4118;PHSRC_HTTP://slp2p.gzport.com:3443");
        data.put("license","SHA1:usigrajf;ye89ALSBO4/08sh7MPuf3BAWXpI=");
        json.put("data",data);
        
        try {
            session.getBasicRemote().sendText(json.toJSONString());
            return this.waitForResponse();
        }catch (Exception e){
            log.error("loginWithLicense异常！");
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 创建远控会话
     * @return
     */
    public String createSession(){
        JSONObject json = new JSONObject();
        json.put("id",id++);
        json.put("name","newsession");
        JSONObject data = new JSONObject();
        data.put("type",0);
        json.put("data",data);
        
        try {
            session.getBasicRemote().sendText(json.toJSONString());
            return this.waitForResponse();
        }catch (Exception e){
            log.error("createSession异常！");
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 销毁远控会话
     * @return
     */
    public String destroySession(){
        if (sessionId < 0){
            return "会话未创建！";
        }
        JSONObject json = new JSONObject();
        json.put("id",id++);
        json.put("name","destroysession");
        JSONObject data = new JSONObject();
        data.put("sessionid",sessionId);
        json.put("data",data);
        
        try {
            session.getBasicRemote().sendText(json.toJSONString());
            return this.waitForResponse();
        }catch (Exception e){
            log.error("destroySession异常！");
            log.error(e.getMessage());
            return null;
        }
    }
    
    /**
     * 登出向日葵sdk
     * @return
     */
    public String logout(){
        JSONObject json = new JSONObject();
        json.put("id",id++);
        json.put("name","logout");

        try {
            session.getBasicRemote().sendText(json.toJSONString());
            return this.waitForResponse();
        }catch (Exception e){
            log.error("logout异常！");
            log.error(e.getMessage());
            return null;
        }
    }
    
    /**
     * 等待服务器返回数据
     * @return
     * @throws Exception
     */
    public String waitForResponse() throws Exception {
        int countdown = 0;
        // 当检测到receivedMessage不为null时，说明服务器返回了数据，进行消费并重置receivedMessage为null
        while (StringUtils.isEmpty(receivedMessage)){
            Thread.sleep(1);
            countdown += 1;
            if (countdown > 500){
                throw new Exception("WebSocket请求超时！");
            }
        }
        System.out.println("服务器返回数据: " + receivedMessage);
        System.out.println("耗时：" + countdown + "ms");
        String message = receivedMessage;
        receivedMessage = null;
        return message;
    }
    
    /**
     * 注册远控服务
     */
    public void installService(){
        try {
            // 判断session是否已创建
            if (session == null || !session.isOpen()){
                this.connect();
            }

            // 登出并重新登录，确保当前是已登录状态，同时登出操作还能清除之前已生成的远控会话信息
            String logout = this.logout();
            // 重置指令编号id
            this.id = 0;
            
            // 使用license登录
            String loginWithLicense = this.loginWithLicense();
            // 创建新会话
            String createSession = this.createSession();
            
            // createSession里的远控会话信息是乱码，不能直接使用。等200ms等向日葵服务器处理完session信息
            Thread.sleep(200);
            // 通过getStatus接口获取远控会话信息
            String status = this.getStatus();
            // 获取会话信息
            JSONObject data = JSONObject.parseObject(status).getJSONObject("data");
            JSONObject sessionData = JSONObject.parseObject(JSONObject.toJSONString(data.getJSONArray("sessions").get(0)));
            String address = sessionData.getString("addr");
            String cid = sessionData.getString("name");
            // 保存新对话的sessionId，用于后续注销服务时销毁对话
            this.sessionId = sessionData.getIntValue("sessionid");
            
            JSONObject sessionInfo = new JSONObject();
            sessionInfo.put("address",address);
            sessionInfo.put("cid",cid);
            
            JSONObject deviceInfo = infoGetter.getDeviceInfo();
            String deviceClientId = (String) deviceInfo.get("deviceClientId");
            sessionInfo.put("deviceClientId",deviceClientId);
            // 添加msg便于主控端进行校验
            sessionInfo.put("msg","连接发起成功！");
            
            // 调用主控端接口发送给主控端
            String result = restTemplate.postForObject(installServiceUrl, sessionInfo, String.class);
            log.info("调用主控端接口结果：" +  result);
            log.info("成功生成远控会话信息并推送至主控端！远控会话信息：" + sessionInfo);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    /**
     * 注销远控服务
     */
    public void uninstallService(){
        try {
            // 判断session是否已创建
            // 因为注册服务时已经确保了WebSocket连接已建立，且远控过程中需要保证被控端一直为登录状态，所以此处一般情况下是已经正常连接的
            if (session == null || !session.isOpen()){
                this.connect();
            }

            String destroySession = this.destroySession();
            if (!destroySession.equals("会话未创建！")){
                JSONObject deviceInfo = infoGetter.getDeviceInfo();
                String deviceClientId = (String) deviceInfo.get("deviceClientId");
                
                JSONObject uninstallResponse = new JSONObject();
                uninstallResponse.put("deviceClientId",deviceClientId);
                uninstallResponse.put("msg",deviceClientId + "的远控服务注销成功！");
                
                // 调用主控端接口发送给主控端
                String result = restTemplate.postForObject(uninstallServiceUrl, uninstallResponse, String.class);
                log.info("调用主控端接口结果：" +  result);
                log.info("远控服务注销成功！");
                
                this.logout();
                this.getStatus();
                // 登出后重置指令id，避免id超长
                this.id = 0;
                
                // 关闭WebSocket连接
                session.close();
            }else {
                log.info("远控服务注销失败！");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    /**
     * 判断是否正在被控
     * @return
     */
    public String judgeIsControlling(){ 
        String status = this.getStatus();
        JSONObject statusObject = JSONObject.parseObject(status);
        // 主控端开始远控后，被控端第一次调用get_status接口时，会返回一个远控状态事件，而不是对应的状态信息，所以需要再次调用get_status接口
        if (!"get_status".equals(statusObject.getString("name"))){
            status = this.getStatus();
            statusObject = JSONObject.parseObject(status);
        }
        List<String> sessionList = statusObject.getJSONObject("data").getJSONArray("sessions").toJavaList(String.class);
        // 2表示正在被控，1表示未被控
        return sessionList.size() > 0 ? "2" : "1";
    }
    
    /**
     * 接收到MQTT指令时，执行相应的操作
     * @param instruction
     */
    public void handlerMqttInstruction(String instruction){
        if (instruction.equals("install")){
            this.installService();
        }
        if (instruction.equals("uninstall")){
            this.uninstallService();
        }
    }

    /**
     * 生成本机状态信息，通过SendAliveMsg定时任务发送存活信息到主控端
     * @return
     */
    public ComputerInfo getComputerInfo(){
        if (this.id > 999999L){
            // 重置id，避免id超长
            this.id = 0;
        }
        
        ComputerInfo computerInfo = new ComputerInfo();
        JSONObject deviceInfo = infoGetter.getDeviceInfo();
        computerInfo.setDeviceClientId((String) deviceInfo.get("deviceClientId"));
        computerInfo.setDeviceName((String) deviceInfo.get("deviceName"));
        computerInfo.setMsgTime(new Date());
        computerInfo.setStatus(this.judgeIsControlling());
        computerInfo.setHardDiskInfo(infoGetter.getHardDiskInfo());
        return computerInfo;
    }
}

